package com.bytecodezz.app.base.toolset.process

import android.app.ActivityManager
import android.app.Application
import android.content.Context
import android.os.Build
import android.os.Process
import android.text.TextUtils
import androidx.annotation.NonNull
import androidx.annotation.Nullable
import java.lang.reflect.Method


/**
 * @author: bytecodezz
 * @version: 1.0
 * @time: 2021/7/14 14:57
 * @package: com.bytecodezz.app.base.toolset.process
 *
 * @description:
 *
 */
class ProcessTools {
    companion object {
        //该变量保存的是当前进程名，如果在多进程中调用，那么他将被加载进不同进程的内存空间，所以可以直接在不同进程中存储对应进程名
        private var currentProcessName: String? = null

        /**
         * @return 当前进程名
         * 性能测试：提速 0.5毫秒~1.0毫秒
         * ProcessUtils.getCurrentProcessNameByApplication()     耗时 654000纳秒=0.654毫秒   (api 28才能用)
         * ProcessUtils.getCurrentProcessNameByActivityThread()  耗时 1121000纳秒=1.121毫秒  (反射)
         * ProcessUtils.getCurrentProcessNameByActivityManager() 耗时 1661000纳秒=1.661毫秒  (IPC通信)
         */
        @Nullable
        fun getCurrentProcessName(@NonNull context: Context?): String? {
            //如果当前进程名存在，则直接使用即可
            return currentProcessName ?: let {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                    //这个新api获取肯定不会失败的  api-28提供的方法
                    currentProcessName = Application.getProcessName()
                } else {
                    currentProcessName = getCurrentProcessNameByActivityThread()
                    if (TextUtils.isEmpty(currentProcessName)) {
                        context?.let {
                            //如果反射方式获取失败，则进行ActivityManager的IPC通信方式获取
                            currentProcessName = getCurrentProcessNameByActivityManager(it)
                        }
                    }
                }
                return currentProcessName
            }
        }

        /**
         * 通过Application新的API获取进程名，无需反射，无需IPC，效率最高。
         */
        private fun getCurrentProcessNameByApplication(): String? {
            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                Application.getProcessName()
            } else null
        }

        /**
         * 通过反射ActivityThread获取进程名，避免了IPC通信
         */
        private fun getCurrentProcessNameByActivityThread(): String? {
            var processName: String? = null
            try {
                val declaredMethod: Method = Class.forName(
                    "android.app.ActivityThread", false,
                    Application::class.java.classLoader
                )
                    .getDeclaredMethod("currentProcessName", *arrayOfNulls<Class<*>?>(0))
                declaredMethod.isAccessible = true
                val invoke: Any = declaredMethod.invoke(null, arrayOfNulls<Any>(0))
                if (invoke is String) {
                    processName = invoke
                }
            } catch (e: Throwable) {
                e.printStackTrace()
            }
            return processName
        }

        /**
         * 通过ActivityManager 获取进程名，需要IPC通信
         */
        private fun getCurrentProcessNameByActivityManager(@NonNull context: Context): String? {
            val pid: Int = Process.myPid()
            val am = context.getSystemService(Context.ACTIVITY_SERVICE)
            if (am is ActivityManager) {
                am.runningAppProcesses?.forEach {
                    if (it.pid == pid)
                        return it.processName
                }
            }
            return null
        }
    }


}